package com.wang.system.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.github.pagehelper.PageHelper;
import com.wang.common.security.exception.ServiceException;
import com.wang.common.core.constants.Constant;
import com.wang.common.core.enums.ResultCode;
import com.wang.system.domain.question.Question;
import com.wang.system.domain.question.dto.QuestionAddDTO;
import com.wang.system.domain.question.dto.QuestionEditDTO;
import com.wang.system.domain.question.dto.QuestionQueryDTO;
import com.wang.system.domain.question.vo.QuestionDetailVO;
import com.wang.system.domain.question.vo.QuestionVO;
import com.wang.system.domain.question.es.QuestionES;
import com.wang.system.elasticsearch.QuestionRepository;
import com.wang.system.manager.QuestionCacheManager;
import com.wang.system.mapper.QuestionMapper;
import com.wang.system.service.IQuestionService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.HashSet;
import java.util.List;
import java.util.Set;

@Service
public class QuestionServiceImpl implements IQuestionService {

    @Autowired
    private QuestionMapper mapper;

    @Autowired
    private QuestionRepository questionRepository;

    @Autowired
    private QuestionCacheManager questionCacheManager;

    @Override
    public List<QuestionVO> list(QuestionQueryDTO questionQueryDTO) {

        //请求竞赛编辑功能时,为竞赛添加题目时,需要把已经添加过的题目排除出去,添加题目的列表中就会不包含这些题目.
        //每个id之间用;分割

        //用excludedQuestionStr来接收已经添加过的题目列表
        String excludedQuestionStr = questionQueryDTO.getExcludeIdStr();

        if (StrUtil.isNotEmpty(excludedQuestionStr)){
            //如果已添加过的题目列表字符串不为空,那么就将其转换为set<Long>集合.
            String[] excludedQuestions = excludedQuestionStr.split(Constant.SPLIT_SYMBOL);

            Set<Long> excludedIdSet = new HashSet<>();

            for (String question: excludedQuestions){
                //将每个题目id转换成Long型后加入set集合
                excludedIdSet.add(Long.valueOf(question));
            }

            //将得到的set集合再设置回请求参数questionQueryDTO中, 以便在Mapper层中把集合中的这些题目排除掉.
            questionQueryDTO.setExcludeIdSet(excludedIdSet);
        }

        //给PageHelper设置每页的条数和页码
        //startPage()开启分页功能, 后续执行的SQL回自动进行分页处理
        PageHelper.startPage(questionQueryDTO.getPageNum(),questionQueryDTO.getPageSize());

        //执行查询合并返回结果
        return mapper.selectQuestionList(questionQueryDTO);
    }

    @Override
    public boolean add(QuestionAddDTO questionAddDTO) {
        //不允许添加标题重复的题目
        //1.查询是否有标题重复的题目
        List<Question> duplicateQuestionsList = mapper.selectList(new LambdaQueryWrapper<Question>()
                .eq(Question::getTitle,questionAddDTO.getTitle()));

        //如果集合不为空,说明有名称重复的题目,不能添加 --> 抛出异常(资源已存在)
        if (CollectionUtil.isNotEmpty(duplicateQuestionsList)){
            throw new ServiceException(ResultCode.FAILED_RESOURCE_ALREADY_EXISTS);
        }

        //2.创建Question实体类
        Question question = new Question();
        //把questionAddDTO中与Question相同的属性复制到question对象中
        BeanUtil.copyProperties(questionAddDTO,question);

        //3.把题目插入数据库中. insert()方法返回插入成功的条数,用ret来接收.
        int succeedRows = mapper.insert(question);
        if (succeedRows <= 0){ //如果插入成功的条数<=0, 则插入失败, 返回false.
            return false;
        }

        QuestionES questionES = new QuestionES();
        BeanUtil.copyProperties(question,questionES);

        //4.把题目加入ES中.
        questionRepository.save(questionES);

        //5.把题目加入缓存中.
        //由于题目id是由雪花算法生成的,在c端需要获取上一题和下一题的id,所以我们需要缓存
        questionCacheManager.addCache(question.getQuestionId());

        return true;
    }

    @Override
    public QuestionDetailVO detail(Long questionId) {

        //创建一个QuestionDetailVO对象
        QuestionDetailVO questionDetailVO = new QuestionDetailVO();

        //从库中查询出传入的id对应的Question记录
        Question question = mapper.selectById(questionId);

        if (question == null){
            //资源不存在,抛出异常
            throw new ServiceException(ResultCode.FAILED_RESOURCE_NOT_EXISTS);
        }

        //走到这里,说明资源存在
        //把 question 中与 QuestionDetailVO 相同的属性复制到 questionDetailVO 对象中
        BeanUtil.copyProperties(question,questionDetailVO);

        //返回结果
        return questionDetailVO;
    }

    @Override
    public Boolean edit(QuestionEditDTO questionEditDTO) {

        //查询未编辑前的题目
        //查询出 库中 题目id与传入的题目id相等的 question 记录 (未编辑之前的题目)
        Question oldQuestion = mapper.selectOne(new LambdaQueryWrapper<Question>()
                .eq(Question::getQuestionId,questionEditDTO.getQuestionId()));

        if (oldQuestion == null){
            //如果查询结果为空,说明请求的资源不存在
            throw new ServiceException(ResultCode.FAILED_RESOURCE_NOT_EXISTS);
        }

        //如果不为空,说明查到了相应记录
        //对题目信息进行修改
        oldQuestion.setTitle(questionEditDTO.getTitle()); //修改题目
        oldQuestion.setContent(questionEditDTO.getContent()); //修改题目内容
        oldQuestion.setDefaultCode(questionEditDTO.getDefaultCode()); //修改默认代码
        oldQuestion.setDifficulty(questionEditDTO.getDifficulty()); //修改难度
        oldQuestion.setMainFuc(questionEditDTO.getMainFuc()); //修改主函数
        oldQuestion.setQuestionCase(questionEditDTO.getQuestionCase()); //修改测试用例
        oldQuestion.setTimeLimit(questionEditDTO.getTimeLimit()); //修改时间限制
        oldQuestion.setSpaceLimit(questionEditDTO.getSpaceLimit()); //修改空间限制

        QuestionES questionES = new QuestionES();
        BeanUtil.copyProperties(oldQuestion,questionES);


        return update(oldQuestion, questionES);

        //更新数据库中的记录.
    }

    /**
     * 更新数据
     */
    @Transactional(rollbackFor = Exception.class)
    public Boolean update(Question oldQuestion, QuestionES questionES) {
        //更新数据库
        mapper.updateById(oldQuestion);
        //更新es中的数据
        questionRepository.save(questionES);
        //删除缓存
        questionCacheManager.deleteCache(oldQuestion.getQuestionId());

        //先更新数据库再删除缓存,防止数据不一致.
        return true;
    }

    @Override
    public int delete(Long questionId) {

        //首先查询是否有该题目
        Question question = mapper.selectOne(new LambdaQueryWrapper<Question>()
                .eq(Question::getQuestionId, questionId));

        if (question == null){
            //如果查询结果为空,说明资源不存在
            throw new ServiceException(ResultCode.FAILED_RESOURCE_NOT_EXISTS);
        }

        //删除es中的数据
        questionRepository.deleteById(question.getQuestionId());
        //由于题目id是由雪花算法生成的,在c端需要获取上一题和下一题的id,所以我们需要缓存

        //-----(1)先删除数据库-----
        int result = mapper.deleteById(question);

        //-----(2)再删除缓存-----
        questionCacheManager.deleteCache(questionId);

        //根据id删除记录
        return result;
    }
}
